LE7, 10 points, questions.

Coding style: 1 point

Lab Exercise (LE) 7

library(tidyverse)

Q1. OIS: Numerical inference (1 point)

OIS v3 5.44: Teaching descriptive statistics.

A study compared five different methods for teaching descriptive statistics.

What are the hypotheses for evaluating

What are the degrees of freedom associated with the F -test

Suppose the p-value for this test is 0.0168.

ANSWER:

The hypotheses for evaluating if the average test scores are different for the different teaching methods:

Null Hypothesis (\(H_0\)): all means across all 5 methods are the same (\(\mu_1 = \mu_2 = \mu_3 = \mu_4 = \mu_5\))

Alternative Hypothesis (\(H_a\)): at least one pair of the means is the same

The degrees of freedom associated with the F-test for evaluating these hypotheses:

Degrees of freedom for treatment: 5-4 = 1

Degrees of freedom for error: 45-5 = 40

The conclusion for this test where the p-value = 0.0168:

Assuming standard significance level of \(\alpha\) = 0.05, reject the null hypothesis becuase p-value < \(\alpha\) => 0.0168 < 0.05


Q2. OIS: Linear regression (1 point)

OIS v3 7.12: Trees.

This dataset

The diameter of the tree is measured

library(tidyverse)
library(ggplot2)

data(trees)
summary(trees)
     Girth           Height       Volume     
 Min.   : 8.30   Min.   :63   Min.   :10.20  
 1st Qu.:11.05   1st Qu.:72   1st Qu.:19.40  
 Median :12.90   Median :76   Median :24.20  
 Mean   :13.25   Mean   :76   Mean   :30.17  
 3rd Qu.:15.25   3rd Qu.:80   3rd Qu.:37.30  
 Max.   :20.60   Max.   :87   Max.   :77.00  

Visualize the relationships in the data

ggplot(trees, aes(x = Volume, y = Girth)) + ggtitle("Diameter by Volume") + geom_point() + geom_smooth()

ggplot(trees, aes(x = Height, y = Girth)) + ggtitle("Height by Volume") + geom_point() + geom_smooth()

Let’s answer questions using linear regression

Volume and Diameter (Girth): Using the geom_smooth function, there is a positive linear association between volume and diameter.

Volume and Height: Using the geom_smooth function, there is a positive linear association between volume and height

Summarizing the model results from the lm() function

girth_lm <- lm(Volume ~ Girth, trees)
height_lm <- lm(Volume ~ Height, trees)

girth_lm

Call:
lm(formula = Volume ~ Girth, data = trees)

Coefficients:
(Intercept)        Girth  
    -36.943        5.066  
height_lm

Call:
lm(formula = Volume ~ Height, data = trees)

Coefficients:
(Intercept)       Height  
    -87.124        1.543  

Volume and Diameter (Girth): using the lm function, we can see that the girth slope coefficient is positive (+5.066), meaning positive association between girth and volume

Volume and Height: using the lm function, we can see that the height slope coefficient is positive (+1.543), meaning positive association between height and volume

The diameter seems to have a stronger positive correlation with volume than height

Suppose you have height and diameter measurements

Which of these variables would be preferable to use

Explain your reasoning

As mentioned before, when looking at the line of association and lower spread within the graph, and comparing with the outputs between the lm function, it’s more preferable to use diameter measurements to predict the volume of timber for the tree using a linear regression model.


Q3. OIS: Logistic regression (1 point)

OIS v3 8.16 Challenger disaster, Part I.

On January 28, 1986, a routine launch was anticipated for the Challenger space shuttle.

Seventy-three seconds into the flight, disaster happened:

An investigation into the cause of the disaster focused on a critical seal called an O-ring, and it is believed that damage to these O-rings during a shuttle launch may be related to the ambient temperature during the launch.

The orings.txt file in the data subfolder

o_rings <- read.table('data/orings.txt', header = TRUE)

Visualize the data. what relationships do you observe between temperature and failure?

ggplot(o_rings, aes(x = temp, y = damage)) + ggtitle("damage by temp") + geom_point() + geom_smooth() + stat_smooth(method = 'glm', family = 'binomial')

it can be seen from the plot that there is a converging (exponentially decaying/logarithmic) relationship between temperature and damage (failure)

Create a logistic regression model.

logistic_challenger <- glm(temp~damage, data = o_rings)
summary(logistic_challenger)

Call:
glm(formula = temp ~ damage, data = o_rings)

Deviance Residuals: 
     Min        1Q    Median        3Q       Max  
-10.3919   -4.4747    0.4426    3.9426    9.4426  

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   71.557      1.272  56.248  < 2e-16 ***
damage        -4.166      1.096  -3.801  0.00104 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for gaussian family taken to be 30.90637)

    Null deviance: 1095.65  on 22  degrees of freedom
Residual deviance:  649.03  on 21  degrees of freedom
AIC: 148.09

Number of Fisher Scoring iterations: 2

Based on the model, do you think concerns regarding O-rings are justified? Explain. what does the p-value tell you?

Damage estimate is -4.166, negative exponential or logarithmic relationship. The logistic regression indicates a lower amount of damaged O-rings for higher temperatures. From the p-value (0.00104 < \(\alpha\) = 0.05), meaning there is an logistic association between damaged O-rings and temperature.

What assumption has to be made for logistic regression to be valid in this case?

The assumption that the relationship with the logarithmic value of the relationship between the damaged O-rings and temperature being linear and independent variable outcomes has to be made for logistic regression to be valid. From what we can see in the graph and the glm function, this assumption has been satisfied and makes the logistic regression being performed valid.


Q4. Logistic regression: Palmer’s penguins (3 points)

Let’s make some logistic models using Palmer’s penguins.

We will used the package nnet

library(nnet)
library(caret)
glimpse(palmerpenguins::penguins)
Rows: 344
Columns: 8
$ species           <fct> Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Adel…
$ island            <fct> Torgersen, Torgersen, Torgersen, Torgersen, Torgersen, Torgersen, Torgersen, Torgersen, Torg…
$ bill_length_mm    <dbl> 39.1, 39.5, 40.3, NA, 36.7, 39.3, 38.9, 39.2, 34.1, 42.0, 37.8, 37.8, 41.1, 38.6, 34.6, 36.6…
$ bill_depth_mm     <dbl> 18.7, 17.4, 18.0, NA, 19.3, 20.6, 17.8, 19.6, 18.1, 20.2, 17.1, 17.3, 17.6, 21.2, 21.1, 17.8…
$ flipper_length_mm <int> 181, 186, 195, NA, 193, 190, 181, 195, 193, 190, 186, 180, 182, 191, 198, 185, 195, 197, 184…
$ body_mass_g       <int> 3750, 3800, 3250, NA, 3450, 3650, 3625, 4675, 3475, 4250, 3300, 3700, 3200, 3800, 4400, 3700…
$ sex               <fct> male, female, female, NA, female, male, female, male, NA, NA, NA, NA, female, male, male, fe…
$ year              <int> 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 20…
df_penguins <- palmerpenguins::penguins

Q4.1 Setup Training & Testing Dataframes

Processing the data

  • divide the data into training and testing datasets
    • Using caret::createDataPartiton()
    • Which will divide the data into groups
      • So we can “train” the model on one subset
      • And use the other subset to “test” the models accuracy
# perform and 80/20 split on penguins data
# goal: determine species of penguins
df_penguins$species <- as.factor(df_penguins$species)
partition <- createDataPartition(df_penguins$species, 
                                 p = 0.8, 
                                 list = FALSE)

penguins_train <- df_penguins[partition, ]
penguins_test <- df_penguins[-partition, ]
test_species <- penguins_test$species
penguins_test <- penguins_test[ , -1]

glimpse(penguins_train)
Rows: 277
Columns: 8
$ species           <fct> Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Adel…
$ island            <fct> Torgersen, Torgersen, Torgersen, Torgersen, Torgersen, Torgersen, Torgersen, Torgersen, Torg…
$ bill_length_mm    <dbl> 39.5, 40.3, 36.7, 39.2, 34.1, 42.0, 37.8, 37.8, 41.1, 38.6, 34.6, 36.6, 42.5, 46.0, 37.8, 37…
$ bill_depth_mm     <dbl> 17.4, 18.0, 19.3, 19.6, 18.1, 20.2, 17.1, 17.3, 17.6, 21.2, 21.1, 17.8, 20.7, 21.5, 18.3, 18…
$ flipper_length_mm <int> 186, 195, 193, 195, 193, 190, 186, 180, 182, 191, 198, 185, 197, 194, 174, 180, 189, 185, 18…
$ body_mass_g       <int> 3800, 3250, 3450, 4675, 3475, 4250, 3300, 3700, 3200, 3800, 4400, 3700, 4500, 4200, 3400, 36…
$ sex               <fct> female, female, female, male, NA, NA, NA, NA, female, male, male, female, male, male, female…
$ year              <int> 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 20…
glimpse(penguins_test)
Rows: 67
Columns: 7
$ island            <fct> Torgersen, Torgersen, Torgersen, Torgersen, Torgersen, Torgersen, Biscoe, Dream, Dream, Drea…
$ bill_length_mm    <dbl> 39.1, NA, 39.3, 38.9, 38.7, 34.4, 38.8, 39.5, 38.8, 40.8, 44.1, 39.6, 37.5, 35.7, 37.6, 45.8…
$ bill_depth_mm     <dbl> 18.7, NA, 20.6, 17.8, 19.0, 18.4, 17.2, 17.8, 20.0, 18.4, 19.7, 18.8, 18.9, 16.9, 17.0, 18.9…
$ flipper_length_mm <int> 181, NA, 190, 181, 195, 184, 180, 188, 190, 195, 196, 190, 179, 185, 185, 197, 189, 190, 186…
$ body_mass_g       <int> 3750, NA, 3650, 3625, 3450, 3325, 3800, 3300, 3950, 3900, 4400, 4600, 2975, 3150, 3600, 4150…
$ sex               <fct> male, NA, male, female, female, female, male, female, male, male, male, male, NA, female, fe…
$ year              <int> 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2008, 2008, 20…

Q4.2 Build a logistic regression model

Build a logistic model

  • To predict species
  • Based on all the predictors in the dataset
    • The short hand way of doing this is to use
    • model_function_call(Response_Variable ~ .)
    • Where the . will automatically use the rest of the columns
      • As predictors

Once you have obtained your model object

  • It’s time to use stats::predict()
    • Which takes in a model object
    • Then applies it to new data (your testing subset)
  • Different model types can have different prediction classes
    • Here we are trying to predict class
set.seed(69)
# Build your model
penguins_model <- multinom(species ~ ., penguins_train, model = FALSE)
# weights:  30 (18 variable)
initial  value 296.625318 
iter  10 value 37.637790
iter  20 value 1.537253
iter  30 value 0.003887
final  value 0.000061 
converged
cat("\nmodel summary: \n")

model summary: 
summary(penguins_model)
Call:
multinom(formula = species ~ ., data = penguins_train, model = FALSE)

Coefficients:
          (Intercept) islandDream islandTorgersen bill_length_mm bill_depth_mm flipper_length_mm body_mass_g   sexmale
Chinstrap   0.1004982    82.93313        27.57785       16.60628     -23.30336         0.4062036 -0.03206725 -22.61880
Gentoo      0.2181655   -10.94114       -44.44507       11.09083     -16.05708        -0.3629314  0.05432559 -33.56594
                year
Chinstrap -0.1651320
Gentoo    -0.1776766

Std. Errors:
          (Intercept) islandDream islandTorgersen bill_length_mm bill_depth_mm flipper_length_mm body_mass_g   sexmale
Chinstrap 0.006426564   0.2000826             NaN      1.7912758     1.1205803         39.210957    2.236183 0.1697706
Gentoo    0.030055185   0.1696553    1.554182e-18      0.2950739     0.7760304          5.910384   25.562279 0.1697717
              year
Chinstrap  7.18726
Gentoo    60.35069

Residual Deviance: 0.000122519 
AIC: 36.00012 
# Predict classes
predictions <- predict(penguins_model, penguins_test)

# predictions vs actual
cat("\npredicted: \n")

predicted: 
predictions
 [1] Adelie    <NA>      Adelie    Adelie    Adelie    Adelie    Adelie    Adelie    Adelie    Adelie    Adelie   
[12] Adelie    <NA>      Adelie    Adelie    Adelie    Adelie    Adelie    Adelie    Adelie    Adelie    Adelie   
[23] Adelie    Adelie    Adelie    Adelie    Adelie    Adelie    Adelie    Adelie    Gentoo    Gentoo    Gentoo   
[34] Gentoo    Gentoo    Gentoo    Gentoo    Gentoo    Gentoo    Gentoo    Gentoo    Gentoo    Gentoo    Gentoo   
[45] Gentoo    Gentoo    Gentoo    Gentoo    Gentoo    Gentoo    Gentoo    <NA>      Gentoo    <NA>      Chinstrap
[56] Chinstrap Chinstrap Chinstrap Chinstrap Chinstrap Chinstrap Chinstrap Chinstrap Chinstrap Chinstrap Chinstrap
[67] Chinstrap
Levels: Adelie Chinstrap Gentoo
cat("\nactual: \n")

actual: 
test_species
 [1] Adelie    Adelie    Adelie    Adelie    Adelie    Adelie    Adelie    Adelie    Adelie    Adelie    Adelie   
[12] Adelie    Adelie    Adelie    Adelie    Adelie    Adelie    Adelie    Adelie    Adelie    Adelie    Adelie   
[23] Adelie    Adelie    Adelie    Adelie    Adelie    Adelie    Adelie    Adelie    Gentoo    Gentoo    Gentoo   
[34] Gentoo    Gentoo    Gentoo    Gentoo    Gentoo    Gentoo    Gentoo    Gentoo    Gentoo    Gentoo    Gentoo   
[45] Gentoo    Gentoo    Gentoo    Gentoo    Gentoo    Gentoo    Gentoo    Gentoo    Gentoo    Gentoo    Chinstrap
[56] Chinstrap Chinstrap Chinstrap Chinstrap Chinstrap Chinstrap Chinstrap Chinstrap Chinstrap Chinstrap Chinstrap
[67] Chinstrap
Levels: Adelie Chinstrap Gentoo

ANSWER: Looking at the output of the neural net model, we can see all the metadata for training, what general activation function (softmax function) is used, and other inputs that can be modified (weights as well). Using a seed here to ensure same responses after multiple reruns

Q4.3 Evaluate accuracy on your test data

Evaluate the accuracy of your model against test data

  • create a confusion matrix to evaluate your results
  • using caret::confusionMatrix()
    • This compares the predicted class against the actual class
    • Which shows how the model classified the data
# comparison of predictions vs actual
confusionMatrix(predictions, reference = test_species)
Confusion Matrix and Statistics

           Reference
Prediction  Adelie Chinstrap Gentoo
  Adelie        28         0      0
  Chinstrap      0        13      0
  Gentoo         0         0     22

Overall Statistics
                                     
               Accuracy : 1          
                 95% CI : (0.9431, 1)
    No Information Rate : 0.4444     
    P-Value [Acc > NIR] : < 2.2e-16  
                                     
                  Kappa : 1          
                                     
 Mcnemar's Test P-Value : NA         

Statistics by Class:

                     Class: Adelie Class: Chinstrap Class: Gentoo
Sensitivity                 1.0000           1.0000        1.0000
Specificity                 1.0000           1.0000        1.0000
Pos Pred Value              1.0000           1.0000        1.0000
Neg Pred Value              1.0000           1.0000        1.0000
Prevalence                  0.4444           0.2063        0.3492
Detection Rate              0.4444           0.2063        0.3492
Detection Prevalence        0.4444           0.2063        0.3492
Balanced Accuracy           1.0000           1.0000        1.0000

Are there any things that were commonly misclassified?

  • Why do you think the model had trouble with these?
  • What can be done to improve this model?

ANSWER:

The model was able to predict the species with 96.97% (close to 100%) accuracy. There wasn’t really anything commonly misclassified, and looking at the statistics from the confusion matrix, only 2 of the 13 variables were improperly classified (2 Chinstraps were classified as Adelie), making chinstrap have a sensitivity at 84%. Probably one of the best ways to improve the model is setting custom initial weights when training to ensure quick convergence, thus eliminating the weights to be set at random, which may or may not be good.


Q5 Houston Crime Reports

We will be working with the Huston crime data file provided by the ggmap package, ggmap::crimes.

This CSV file contains the location (latitude and longitude) for crimes reported from January 2010 - August 2010

library(ggmap)
rgdal_show_exportToProj4_warnings = "none"
library(sp)
library(rgdal)
library(leaflet)
library(lubridate)
library(RColorBrewer)
library(classInt)
library(tidyverse)
library(Rfast)

Q5.2 Geospatial Analysis

library(sp)
library(rgdal)
library(maptools)

Geospatial Analysis

Next we’ll look at the spatial distribution of this data.

  • Plot the data on an OpenStreetMap (Error: ‘get_openstreetmap’ is defunct. Use ‘OSM is at least temporarily not supported, see https://github.com/dkahle/ggmap/issues/117.’ instead. See help(“Defunct”))
    • Using source = "stamen"
  • You will have to specify the location in the function call
    • This is because ggmap::get_map()
    • Defaults to Google Maps
      • when a bounding box is not specified
    • A bounding box
      • Gives the boundaries of the map that is downloaded
      • Specified with a list
      • c(left = '' , right = '' , top = '', bottom = '')
  • Color the map based on
    • Type of Crime Reported
#Get the bbox

bbox <- c(left = -96, 
          right = -95,
          top = 30.25,
          bottom = 29.25)

#Retrieve the map

houstonmap <- get_map(location = bbox,
                      source = 'stamen')

#Plot

crime_report$offense <- as.factor(crime_report$offense)

ggmap(houstonmap) + geom_point(aes(x = lon, y = lat, color = offense), data = crime_report)

ANSWER: plotted

Q5.3a Modify the incident occurrence layer, to better see whats happening

In the last map, it was a bit tricky

  • to see the density of the incidents
    • because all the graphed points
    • were sitting on top of each other.

We’re going to now modify the incident occurrence layer

  • to plot the density of points
    • vs plotting each incident individually.
  • We accomplish this with
    • the ggplot2::stat_density2d() function
    • vs using ggplot2::geom_point().
#Plot using stat_density2d()
ggmap(houstonmap) + stat_density2d(aes(x = lon,
                                       y = lat, 
                                       color = offense), 
                                   data = crime_report)

ANSWER:

Q5.3b What does ..level.. do

  • What does ..level.. do
    • in the ggplot2::stat_density2d() function call?
  • Hint: Look at the help topics for this function
ggmap(houstonmap) + stat_density2d(aes(x = lon, 
                                       y = lat, 
                                       color = offense, 
                                       fill = ..level..), 
                                   data = crime_report
                                   ) + stat_density2d(aes( 
                                       fill = ..level..)
                                       )

ANSWER: ..level.. tells ggmap to reference the column in a newly built data frame, and sketches contour lines

Q5.4 What is the safest and most dangerous neighborhoods

#Filter
library(broom)
library(ggplot2)

Finally

  • Filter out a specific crime of your choosing (auto-theft)
  • Plot the crime density

We will use a new package that assists in geospatial analysis

  • rdgal
  • This package is used to transform and project geospatial objects
  • It also has some nice functions for working with .shp files
    • .shp files contain information about regions on a map
    • i.e .shp files can contain the information
      • the size, shape, and location of countries or states

Add Polygons for the specific Neighborhoods

  • Using NeighborShapefile
    • and rdgal::readOGR()
    • or mapdata::readShapeSpatial()
nbhd_path <- 'data/shp/COH_SUPER_NEIGHBORHOODS.shp'
nbhd_file <- readOGR(nbhd_path)
OGR data source with driver: ESRI Shapefile 
Source: "/Users/momo/Documents/GitRem/22f-dsci351-451-mxd601/1-assignments/lab-exercise/LE7/data/shp/COH_SUPER_NEIGHBORHOODS.shp", layer: "COH_SUPER_NEIGHBORHOODS"
with 88 features
It has 14 fields
nbhds <- tidy(nbhd_file)
names(nbhd_file)
 [1] "OBJECTID"   "PERIMETER"  "POLYID"     "SNBNAME"    "COUNCIL_AC" "RECOGNITIO" "SnbrInfoUR" "WeCan"     
 [9] "Top10"      "CEA_FLAG"   "cohgis_COH" "cohgis_C_1" "ShapeSTAre" "ShapeSTLen"
auto_thefts <- subset(crime_report, offense == 'auto theft')
filtered_auto_thefts <- as.data.frame(as.tibble(auto_thefts) %>% select(number, lon, lat, block, premise))


id_name_map <- data.frame(
  id = nbhd_file$POLYID,
  name = nbhd_file$SNBNAME
)

nbhds <- merge(nbhds, id_name_map, by.x = 'id')

What is the most dangerous Neighborhood for your crime? Where is the safest neighborhood?

Label the map with the neighborhoods

  • Hint: It’s OK to remove some of the labels, there’s a lot
  • ggplot2::geom_text() has a built in function for this
    • check overlap = TRUE
ggmap(houstonmap
) + geom_polygon(data = nbhds, 
                 mapping = aes(x = long, 
                               y = lat, 
                               group = group,
                               fill = id), 
                 show.legend = FALSE
)  + geom_point(data = auto_thefts, 
                mapping = aes(x = lon, 
                              y = lat, 
                              ), 
                color = "lightgrey",
                show.legend = FALSE
) + geom_text(data = nbhds, 
              mapping = aes(x = long, 
                            y = lat, 
                            label = name),
              show.legend = FALSE,
              check_overlap = TRUE,
              # vjust = "inward", 
              # hjust = "inward"
)

ANSWER:

Looks like midtown-willowbrook, and kingwood area are some of the most dangerous neighborhoods for auto-theft, whereas fondren gardens, willowbend area, westbranch, and central southwest are some of the safest.

LS0tCnRpdGxlOiAnQ1dSVSBEU0NJMzUxLTM1MW0tNDUxOiBMYWIgRXhlcmNpc2UgTEU3IE5BTUUnCnN1YnRpdGxlOiAnSW5mZXJlbmNlLCBMaW5lYXIgUmVncmVzc2lvbiwgVGltZXNlcmllcyBBbmFseXNpcycKYXV0aG9yOiAiUHJvZi46Um9nZXIgRnJlbmNoLCBQYXVsIFcuIExldSwgVEE6IFJheW1vbmQgV2llc2VyLCBTYW1lZXJhIE5hbGluIFZlbmthdCIKZGF0ZTogICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCLCAlWScpYCIKb3V0cHV0OiAKICBwZGZfZG9jdW1lbnQ6CiAgICBsYXRleF9lbmdpbmU6IHhlbGF0ZXgKICAgIHRvYzogVFJVRQogICAgbnVtYmVyX3NlY3Rpb25zOiBUUlVFCiAgICB0b2NfZGVwdGg6IDYKICAgIGhpZ2hsaWdodDogdGFuZ28KICBodG1sX25vdGVib29rOgogIGh0bWxfZG9jdW1lbnQ6CiAgICBjc3M6IC4uL2xhYi5jc3MKICAgIGhpZ2hsaWdodDogcHlnbWVudHMKICAgIHRoZW1lOiBjZXJ1bGVhbgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNgogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGRmX3ByaW50OiBwYWdlZAp1cmxjb2xvcjogYmx1ZQphbHdheXNfYWxsb3dfaHRtbDogdHJ1ZQotLS0KClxzZXRjb3VudGVye3NlY3Rpb259ezd9ClxzZXRjb3VudGVye3N1YnNlY3Rpb259ezB9CgpgYGB7ciBzZXR1cCwgaW5jbHVkZSA9IEZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgY2FjaGUgPSBGQUxTRSwgIyBpZiBUUlVFIGtuaXRyIHdpbGwgY2FjaGUgcmVzdWx0cyB0byByZXVzZSBpbiBmdXR1cmUga25pdHMKICBmaWcud2lkdGggPSA2LCAjIHRoZSB3aWR0aCBmb3IgcGxvdHMgY3JlYXRlZCBieSBjb2RlIGNodW5rCiAgZmlnLmhlaWdodCA9IDQsICMgdGhlIGhlaWdodCBmb3IgcGxvdHMgY3JlYXRlZCBieSBjb2RlIGNodW5rCiAgZmlnLmFsaWduID0gJ2NlbnRlcicsICMgaG93IHRvIGFsaWduIGdyYXBoaWNzLiAnbGVmdCcsICdyaWdodCcsICdjZW50ZXInCiAgZHBpID0gMzAwLCAKICBkZXYgPSAncG5nJywgIyBNYWtlcyBlYWNoIGZpZyBhIHBuZywgYW5kIGF2b2lkcyBwbG90dGluZyBldmVyeSBkYXRhIHBvaW50CiAgIyBldmFsID0gRkFMU0UsICMgaWYgRkFMU0UsIHRoZW4gdGhlIFIgY29kZSBjaHVua3MgYXJlIG5vdCBldmFsdWF0ZWQKICAjIHJlc3VsdHMgPSAnYXNpcycsICMga25pdHIgcGFzc2VzIHRocm91Z2ggcmVzdWx0cyB3aXRob3V0IHJlZm9ybWF0dGluZwogIGVjaG8gPSBUUlVFLCAjIGlmIEZBTFNFIGtuaXRyIHdvbid0IGRpc3BsYXkgY29kZSBpbiBjaHVuayBhYm92ZSBpdCdzIHJlc3VsdHMKICBtZXNzYWdlID0gRkFMU0UsICMgaWYgRkFMU0Uga25pdHIgd29uJ3QgZGlzcGxheSBtZXNzYWdlcyBnZW5lcmF0ZWQgYnkgY29kZQogIHN0cmlwLndoaXRlID0gVFJVRSwgIyBpZiBGQUxTRSBrbml0ciB3b24ndCByZW1vdmUgd2hpdGUgc3BhY2VzIGF0IGJlZyBvciBlbmQgb2YgY29kZSBjaHVuawogIHdhcm5pbmcgPSBGQUxTRSwgIyBpZiBGQUxTRSBrbml0ciB3b24ndCBkaXNwbGF5IHdhcm5pbmcgbWVzc2FnZXMgaW4gdGhlIGRvYwogIGVycm9yID0gVFJVRSkgIyByZXBvcnQgZXJyb3JzCiAgIyBvcHRpb25zKHRpbnl0ZXgudmVyYm9zZSA9IFRSVUUpCmBgYAoKIyMjIExFNywgMTAgcG9pbnRzLCAgcXVlc3Rpb25zLiAKCkNvZGluZyBzdHlsZTogMSBwb2ludAoKICAtIFExIC0gT0lTOiBOdW1lcmljYWwgaW5mZXJlbmNlLCAxIHB0LgogIC0gUTIgLSBPSVM6IExpbmVhciByZWdyZXNzaW9uLCAxIHB0LgogIC0gUTMgLSBPSVM6IExvZ2lzdGljIHJlZ3Jlc3Npb24sIDEgcHQuCiAgLSBRNCAtIExvZ2lzdGljIHJlZ3Jlc3Npb246IFBhbG1lcidzIHBlbmd1aW5zLCAzIHB0cy4KICAtIFE1IC0gSG91c3RvbiBjcmltZSBkYXRhLCAzIHB0cy4KCiMjIyMgTGFiIEV4ZXJjaXNlIChMRSkgNwoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBRMS4gT0lTOiBOdW1lcmljYWwgaW5mZXJlbmNlICgxIHBvaW50KQoKT0lTIHYzIDUuNDQ6IFRlYWNoaW5nIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3MuCgpBIHN0dWR5IGNvbXBhcmVkIGZpdmUgZGlmZmVyZW50IG1ldGhvZHMgZm9yIHRlYWNoaW5nIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3MuIAoKICAtIFRoZSBmaXZlIG1ldGhvZHMgd2VyZSAKICAgIC0gdHJhZGl0aW9uYWwgbGVjdHVyZSBhbmQgZGlzY3Vzc2lvbiwgCiAgICAtIHByb2dyYW1tZWQgdGV4dGJvb2sgaW5zdHJ1Y3Rpb24sIAogICAgLSBwcm9ncmFtbWVkIHRleHQgd2l0aCBsZWN0dXJlcywgCiAgICAtIGNvbXB1dGVyIGluc3RydWN0aW9uLCAKICAgIC0gYW5kIGNvbXB1dGVyIGluc3RydWN0aW9uIHdpdGggbGVjdHVyZXMuIAoKICAtIDQ1IHN0dWRlbnRzIHdlcmUgcmFuZG9tbHkgYXNzaWduZWQsIAogICAgLSA5IHRvIGVhY2ggbWV0aG9kLiAKICAtIEFmdGVyIGNvbXBsZXRpbmcgdGhlIGNvdXJzZSwgCiAgICAtIHN0dWRlbnRzIHRvb2sgYSAxLWhvdXIgZXhhbS4KCldoYXQgYXJlIHRoZSBoeXBvdGhlc2VzIGZvciBldmFsdWF0aW5nIAogIAogIC0gaWYgdGhlIGF2ZXJhZ2UgdGVzdCBzY29yZXMgYXJlIGRpZmZlcmVudCAKICAgIC0gZm9yIHRoZSBkaWZmZXJlbnQgdGVhY2hpbmcgbWV0aG9kcz8KCldoYXQgYXJlIHRoZSBkZWdyZWVzIG9mIGZyZWVkb20gYXNzb2NpYXRlZCB3aXRoIHRoZSBGIC10ZXN0IAoKICAtIGZvciBldmFsdWF0aW5nIHRoZXNlIGh5cG90aGVzZXM/CgpTdXBwb3NlIHRoZSBwLXZhbHVlIGZvciB0aGlzIHRlc3QgaXMgMC4wMTY4LiAKCiAgLSBXaGF0IGlzIHRoZSBjb25jbHVzaW9uPwoKQU5TV0VSOgoKVGhlIGh5cG90aGVzZXMgZm9yIGV2YWx1YXRpbmcgaWYgdGhlIGF2ZXJhZ2UgdGVzdCBzY29yZXMgYXJlIGRpZmZlcmVudCBmb3IgdGhlIGRpZmZlcmVudCB0ZWFjaGluZyBtZXRob2RzOgoKTnVsbCBIeXBvdGhlc2lzICgkSF8wJCk6IGFsbCBtZWFucyBhY3Jvc3MgYWxsIDUgbWV0aG9kcyBhcmUgdGhlIHNhbWUgKCRcbXVfMSA9IFxtdV8yID0gXG11XzMgPSBcbXVfNCA9IFxtdV81JCkKCkFsdGVybmF0aXZlIEh5cG90aGVzaXMgKCRIX2EkKTogYXQgbGVhc3Qgb25lIHBhaXIgb2YgdGhlIG1lYW5zIGlzIHRoZSBzYW1lCgpUaGUgZGVncmVlcyBvZiBmcmVlZG9tIGFzc29jaWF0ZWQgd2l0aCB0aGUgRi10ZXN0IGZvciBldmFsdWF0aW5nIHRoZXNlIGh5cG90aGVzZXM6CgpEZWdyZWVzIG9mIGZyZWVkb20gZm9yIHRyZWF0bWVudDogNS00ID0gMQoKRGVncmVlcyBvZiBmcmVlZG9tIGZvciBlcnJvcjogNDUtNSA9IDQwCgpUaGUgY29uY2x1c2lvbiBmb3IgdGhpcyB0ZXN0IHdoZXJlIHRoZSBwLXZhbHVlID0gMC4wMTY4OgoKQXNzdW1pbmcgc3RhbmRhcmQgc2lnbmlmaWNhbmNlIGxldmVsIG9mICRcYWxwaGEkID0gMC4wNSwgcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMgYmVjdWFzZSBwLXZhbHVlIDwgJFxhbHBoYSQgPT4gMC4wMTY4IDwgMC4wNQogIAotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgUTIuIE9JUzogTGluZWFyIHJlZ3Jlc3Npb24gICAoMSBwb2ludCkKCk9JUyB2MyA3LjEyOiBUcmVlcy4gCgpUaGlzIGRhdGFzZXQgCgogIC0gc2hvd3MgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIAogICAgLSBoZWlnaHQsIAogICAgLSBkaWFtZXRlciAoZ2lydGgpLCAKICAgIC0gYW5kIHZvbHVtZSBvZiB0aW1iZXIgCiAgLSBpbiAzMSBmZWxsZWQgYmxhY2sgY2hlcnJ5IHRyZWVzLiAKICAKVGhlIGRpYW1ldGVyIG9mIHRoZSB0cmVlIGlzIG1lYXN1cmVkIAoKICAtIDQuNSBmZWV0IGFib3ZlIHRoZSBncm91bmQuCiAgCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ3Bsb3QyKQoKZGF0YSh0cmVlcykKc3VtbWFyeSh0cmVlcykKYGBgCgpWaXN1YWxpemUgdGhlIHJlbGF0aW9uc2hpcHMgaW4gdGhlIGRhdGEKCiAgLSBoZWlnaHQgdnMuIHZvbHVtZQogIC0gZGlhbWV0ZXIgdnMuIHZvbHVtZQogIC0geW91ciB2aXN1YWxpemF0aW9ucyBzaG91bGQgY29udGFpbiBhbGwgaW1wb3J0YW50IGluZm8gKGxhYmVscywgZXRjLikKICAtIChoaW50OiBzY2F0dGVycGxvdHMpCgpgYGB7cn0KZ2dwbG90KHRyZWVzLCBhZXMoeCA9IFZvbHVtZSwgeSA9IEdpcnRoKSkgKyBnZ3RpdGxlKCJEaWFtZXRlciBieSBWb2x1bWUiKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkKZ2dwbG90KHRyZWVzLCBhZXMoeCA9IEhlaWdodCwgeSA9IEdpcnRoKSkgKyBnZ3RpdGxlKCJIZWlnaHQgYnkgVm9sdW1lIikgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpCmBgYAogIApMZXQncyBhbnN3ZXIgcXVlc3Rpb25zIHVzaW5nIGxpbmVhciByZWdyZXNzaW9uCgogIC0gRGVzY3JpYmUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHZvbHVtZSBhbmQgaGVpZ2h0IG9mIHRoZXNlIHRyZWVzLgogIC0gRGVzY3JpYmUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHZvbHVtZSBhbmQgZGlhbWV0ZXIgb2YgdGhlc2UgdHJlZXMuCiAgClZvbHVtZSBhbmQgRGlhbWV0ZXIgKEdpcnRoKTogVXNpbmcgdGhlIGdlb21fc21vb3RoIGZ1bmN0aW9uLCB0aGVyZSBpcyBhIHBvc2l0aXZlIGxpbmVhciBhc3NvY2lhdGlvbiBiZXR3ZWVuIHZvbHVtZSBhbmQgZGlhbWV0ZXIuCgpWb2x1bWUgYW5kIEhlaWdodDogVXNpbmcgdGhlIGdlb21fc21vb3RoIGZ1bmN0aW9uLCB0aGVyZSBpcyBhIHBvc2l0aXZlIGxpbmVhciBhc3NvY2lhdGlvbiBiZXR3ZWVuIHZvbHVtZSBhbmQgaGVpZ2h0CgpTdW1tYXJpemluZyB0aGUgbW9kZWwgcmVzdWx0cyBmcm9tIHRoZSBgbG0oKWAgZnVuY3Rpb24gCgogIC0gd2lsbCBwcm92aWRlIHZhbHVhYmxlIG51bWVyaWNhbCBpbnNpZ2h0cy4KCmBgYHtyfQpnaXJ0aF9sbSA8LSBsbShWb2x1bWUgfiBHaXJ0aCwgdHJlZXMpCmhlaWdodF9sbSA8LSBsbShWb2x1bWUgfiBIZWlnaHQsIHRyZWVzKQoKZ2lydGhfbG0KaGVpZ2h0X2xtCmBgYAoKVm9sdW1lIGFuZCBEaWFtZXRlciAoR2lydGgpOiB1c2luZyB0aGUgbG0gZnVuY3Rpb24sIHdlIGNhbiBzZWUgdGhhdCB0aGUgZ2lydGggc2xvcGUgY29lZmZpY2llbnQgaXMgcG9zaXRpdmUgKCs1LjA2NiksIG1lYW5pbmcgcG9zaXRpdmUgYXNzb2NpYXRpb24gYmV0d2VlbiBnaXJ0aCBhbmQgdm9sdW1lCgpWb2x1bWUgYW5kIEhlaWdodDogdXNpbmcgdGhlIGxtIGZ1bmN0aW9uLCB3ZSBjYW4gc2VlIHRoYXQgdGhlIGhlaWdodCBzbG9wZSBjb2VmZmljaWVudCBpcyBwb3NpdGl2ZSAoKzEuNTQzKSwgbWVhbmluZyBwb3NpdGl2ZSBhc3NvY2lhdGlvbiBiZXR3ZWVuIGhlaWdodCBhbmQgdm9sdW1lCgpUaGUgZGlhbWV0ZXIgc2VlbXMgdG8gaGF2ZSBhIHN0cm9uZ2VyIHBvc2l0aXZlIGNvcnJlbGF0aW9uIHdpdGggdm9sdW1lIHRoYW4gaGVpZ2h0CgpTdXBwb3NlIHlvdSBoYXZlIGhlaWdodCBhbmQgZGlhbWV0ZXIgbWVhc3VyZW1lbnRzIAoKICAtIGZvciBhbm90aGVyIGJsYWNrIGNoZXJyeSB0cmVlLiAKICAKV2hpY2ggb2YgdGhlc2UgdmFyaWFibGVzIHdvdWxkIGJlIHByZWZlcmFibGUgdG8gdXNlIAoKICAtIHRvIHByZWRpY3QgdGhlIHZvbHVtZSBvZiB0aW1iZXIgaW4gdGhpcyB0cmVlIAogIC0gdXNpbmcgYSBzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWw/IAogIApFeHBsYWluIHlvdXIgcmVhc29uaW5nCgpBcyBtZW50aW9uZWQgYmVmb3JlLCB3aGVuIGxvb2tpbmcgYXQgdGhlIGxpbmUgb2YgYXNzb2NpYXRpb24gYW5kIGxvd2VyIHNwcmVhZCB3aXRoaW4gdGhlIGdyYXBoLCBhbmQgY29tcGFyaW5nIHdpdGggdGhlIG91dHB1dHMgYmV0d2VlbiB0aGUgbG0gZnVuY3Rpb24sIGl0J3MgbW9yZSBwcmVmZXJhYmxlIHRvIHVzZSBkaWFtZXRlciBtZWFzdXJlbWVudHMgdG8gcHJlZGljdCB0aGUgdm9sdW1lIG9mIHRpbWJlciBmb3IgdGhlIHRyZWUgdXNpbmcgYSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbC4gCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgUTMuIE9JUzogTG9naXN0aWMgcmVncmVzc2lvbiAgKDEgcG9pbnQpCgpPSVMgdjMgOC4xNiBDaGFsbGVuZ2VyIGRpc2FzdGVyLCBQYXJ0IEkuIAoKT24gSmFudWFyeSAyOCwgMTk4NiwgYSByb3V0aW5lIGxhdW5jaCB3YXMgYW50aWNpcGF0ZWQgZm9yIHRoZSBDaGFsbGVuZ2VyIHNwYWNlIHNodXR0bGUuIAogIApTZXZlbnR5LXRocmVlIHNlY29uZHMgaW50byB0aGUgZmxpZ2h0LCBkaXNhc3RlciBoYXBwZW5lZDogCgogIC0gdGhlIHNodXR0bGUgYnJva2UgYXBhcnQsIAogIC0ga2lsbGluZyBhbGwgc2V2ZW4gY3JldyBtZW1iZXJzIG9uIGJvYXJkLiAKICAKQW4gaW52ZXN0aWdhdGlvbiBpbnRvIHRoZSBjYXVzZSBvZiB0aGUgZGlzYXN0ZXIgZm9jdXNlZCBvbiBhIGNyaXRpY2FsIHNlYWwgY2FsbGVkIGFuIE8tcmluZywgYW5kIGl0IGlzIGJlbGlldmVkIHRoYXQgZGFtYWdlIHRvIHRoZXNlIE8tcmluZ3MgZHVyaW5nIGEgc2h1dHRsZSBsYXVuY2ggbWF5IGJlIHJlbGF0ZWQgdG8gdGhlIGFtYmllbnQgdGVtcGVyYXR1cmUgZHVyaW5nIHRoZSBsYXVuY2guCgpUaGUgb3JpbmdzLnR4dCBmaWxlIGluIHRoZSBgZGF0YWAgc3ViZm9sZGVyIAoKICAtIGNvbnRhaW5zIGRhdGEgb24gCiAgICAtIHRoZSB0ZW1wZXJhdHVyZSBhbmQgCiAgICAtIG51bWJlciBvZiBkYW1hZ2VkIE8tcmluZ3MgCiAgLSBmb3IgMjMgc2h1dHRsZSBtaXNzaW9ucywgCiAgLSB3aGVyZSB0aGUgbWlzc2lvbiBvcmRlciBpcyBiYXNlZCBvbiAKICAgIC0gdGhlIHRlbXBlcmF0dXJlIGF0IHRoZSB0aW1lIG9mIHRoZSBsYXVuY2guIAogIC0gYFRlbXBgIGdpdmVzIHRoZSB0ZW1wZXJhdHVyZSBpbiBGYWhyZW5oZWl0LCAKICAtIGBEYW1hZ2VkYCByZXByZXNlbnRzIHRoZSBudW1iZXIgb2YgZGFtYWdlZCBPLXJpbmdzLiAKICAtIFRoZXJlIGFyZSA2IE8tcmluZ3MgdG90YWwsIAogICAgLSBzbyB0aGUgbnVtYmVyIG9mIHVuZGFtYWdlZCBPLXJpbmdzIGNhbiBiZSBjYWxjdWxhdGVkLgogICAgCmBgYHtyfQpvX3JpbmdzIDwtIHJlYWQudGFibGUoJ2RhdGEvb3JpbmdzLnR4dCcsIGhlYWRlciA9IFRSVUUpCmBgYAoKVmlzdWFsaXplIHRoZSBkYXRhLiB3aGF0IHJlbGF0aW9uc2hpcHMgZG8geW91IG9ic2VydmUgYmV0d2VlbiB0ZW1wZXJhdHVyZSBhbmQgZmFpbHVyZT8KCmBgYHtyfQpnZ3Bsb3Qob19yaW5ncywgYWVzKHggPSB0ZW1wLCB5ID0gZGFtYWdlKSkgKyBnZ3RpdGxlKCJkYW1hZ2UgYnkgdGVtcCIpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArIHN0YXRfc21vb3RoKG1ldGhvZCA9ICdnbG0nLCBmYW1pbHkgPSAnYmlub21pYWwnKQpgYGAKCml0IGNhbiBiZSBzZWVuIGZyb20gdGhlIHBsb3QgdGhhdCB0aGVyZSBpcyBhIGNvbnZlcmdpbmcgKGV4cG9uZW50aWFsbHkgZGVjYXlpbmcvbG9nYXJpdGhtaWMpIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRlbXBlcmF0dXJlIGFuZCBkYW1hZ2UgKGZhaWx1cmUpCgoKQ3JlYXRlIGEgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbC4KCiAgLSBjbGFzc2lmeSBlYWNoIGNhc2UgYXMgaGF2aW5nIGVpdGhlciBkYW1hZ2VkIG9yIHVuZGFtYWdlZCBPLXJpbmdzICgxIG9yIDApCiAgICAtIGEgYmluYXJ5ICJmYWlsdXJlIiB2YXJpYWJsZSB3aWxsIGhlbHAgdXMgCiAgICAtIGRldGVybWluZSBwcm9iYWJpbGl0eSBvZiBmYWlsdXJlIGFzIGEgcmVzdWx0CiAgLSB1c2UgdGVtcGVyYXR1cmUgYXMgYSBwcmVkaWN0b3IKICAtIHVzZSB0aGUgYGdsbSgpYCBmdW5jdGlvbiAKICAtIGRpc3BsYXkgdGhlIHN1bW1hcnkgc3RhdGlzdGljcyBvZiB5b3VyIG1vZGVsCgpgYGB7cn0KbG9naXN0aWNfY2hhbGxlbmdlciA8LSBnbG0odGVtcH5kYW1hZ2UsIGRhdGEgPSBvX3JpbmdzKQpzdW1tYXJ5KGxvZ2lzdGljX2NoYWxsZW5nZXIpCmBgYAoKQmFzZWQgb24gdGhlIG1vZGVsLCBkbyB5b3UgdGhpbmsgY29uY2VybnMgcmVnYXJkaW5nIE8tcmluZ3MgYXJlIGp1c3RpZmllZD8gRXhwbGFpbi4gd2hhdCBkb2VzIHRoZSBwLXZhbHVlIHRlbGwgeW91PwoKRGFtYWdlIGVzdGltYXRlIGlzIC00LjE2NiwgbmVnYXRpdmUgZXhwb25lbnRpYWwgb3IgbG9nYXJpdGhtaWMgcmVsYXRpb25zaGlwLiBUaGUgbG9naXN0aWMgcmVncmVzc2lvbiBpbmRpY2F0ZXMgYSBsb3dlciBhbW91bnQgb2YgZGFtYWdlZCBPLXJpbmdzIGZvciBoaWdoZXIgdGVtcGVyYXR1cmVzLiBGcm9tIHRoZSBwLXZhbHVlICgwLjAwMTA0IDwgJFxhbHBoYSQgPSAwLjA1KSwgbWVhbmluZyB0aGVyZSBpcyBhbiBsb2dpc3RpYyBhc3NvY2lhdGlvbiBiZXR3ZWVuIGRhbWFnZWQgTy1yaW5ncyBhbmQgdGVtcGVyYXR1cmUuCgpXaGF0IGFzc3VtcHRpb24gaGFzIHRvIGJlIG1hZGUgZm9yIGxvZ2lzdGljIHJlZ3Jlc3Npb24gdG8gYmUgdmFsaWQgaW4gdGhpcyBjYXNlPwoKVGhlIGFzc3VtcHRpb24gdGhhdCB0aGUgcmVsYXRpb25zaGlwIHdpdGggdGhlIGxvZ2FyaXRobWljIHZhbHVlIG9mIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgZGFtYWdlZCBPLXJpbmdzIGFuZCB0ZW1wZXJhdHVyZSBiZWluZyBsaW5lYXIgYW5kIGluZGVwZW5kZW50IHZhcmlhYmxlIG91dGNvbWVzIGhhcyB0byBiZSBtYWRlIGZvciBsb2dpc3RpYyByZWdyZXNzaW9uIHRvIGJlIHZhbGlkLiBGcm9tIHdoYXQgd2UgY2FuIHNlZSBpbiB0aGUgZ3JhcGggYW5kIHRoZSBnbG0gZnVuY3Rpb24sIHRoaXMgYXNzdW1wdGlvbiBoYXMgYmVlbiBzYXRpc2ZpZWQgYW5kIG1ha2VzIHRoZSBsb2dpc3RpYyByZWdyZXNzaW9uIGJlaW5nIHBlcmZvcm1lZCB2YWxpZC4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBRNC4gTG9naXN0aWMgcmVncmVzc2lvbjogUGFsbWVyJ3MgcGVuZ3VpbnMgKDMgcG9pbnRzKQoKTGV0J3MgbWFrZSBzb21lIGxvZ2lzdGljIG1vZGVscyB1c2luZyBQYWxtZXIncyBwZW5ndWlucy4KCiAgLSB3ZSd2ZSBsb29rZWQgYXQgcmVncmVzc2lvbiB3aXRoIGEgc2luZ2xlIHByZWRpY3RvcgogIC0gbGV0J3MgbWFrZSBhIGxvZ2lzdGljIG1vZGVsIHdpdGggbXVsdGlwbGUgcHJlZGljdG9ycwogICAgLSB3ZSdyZSBpbmNyZWFzaW5nIHRoZSBkaW1lbnNpb25zIG9mIHRoZSBtb2RlbCAKICAgIC0gaW4gb3JkZXIgdG8gZ2V0IG1vcmUgaW5mb3JtYXRpb24gb3V0IG9mIHRoZSBkYXRhCiAgLSB3ZSB3YW50IHRvIGNyZWF0ZSBhIG1vZGVsIHRoYXQgY2FuIHByZWRpY3QgcGVuZ3VpbiBzcGVjaWVzCiAgCldlIHdpbGwgdXNlZCB0aGUgcGFja2FnZSBgbm5ldGAKCiAgLSBUaGlzIHBhY2thZ2UgaXMgdXNlZCBmb3IgbWFjaGluZSBsZWFybmluZwogIC0gQnV0IGl0IGFsc28gaGFzIGEgZnVuY3Rpb24KICAgIC0gYG5uZXQ6Om11bHRpbm9tKClgCiAgICAgIC0gV2hpY2ggd29ya3MgbGlrZSBgc3RhdHM6OmxtKClgCiAgICAgIC0gc2VlIGA/bm5ldDo6bXVsdGlub20oKWAgZm9yIG1vcmUgaGVscAogIC0gVGhpcyBpcyBiZWNhdXNlIGxvZ2lzdGljIG1vZGVscwogICAgLSBBcmUgdXNlZCB0byBiYXNlbGluZSBtb3JlIGNvbXBsZXggTWFjaGluZSBMZWFybmluZyBNb2RlbHMKICAgICAgLSBGb3IgcGVyZm9ybWFuY2UKICAtIGBubmV0OjptdWx0aW5vbSgpYCBpcyB1c2VkIHRvIGJ1aWxkIG11bHRpcGxlIGxvZ2lzdGljIG1vZGVscwogICAgLSBXaGljaCBjYW4gYmUgdXNlZCB0byBjbGFzc2lmeSBtdWx0aXBsZSBvdXRwdXRzCiAgICAtIEluc3RlYWQgb2YgYSBiaW5hcnkgY2xhc3NpZmljYXRpb24gbW9kZWwgbGlrZSBhIGxvZ2lzdGljIG1vZGVsCgpgYGB7cn0KbGlicmFyeShubmV0KQpsaWJyYXJ5KGNhcmV0KQpnbGltcHNlKHBhbG1lcnBlbmd1aW5zOjpwZW5ndWlucykKCmRmX3Blbmd1aW5zIDwtIHBhbG1lcnBlbmd1aW5zOjpwZW5ndWlucwpgYGAKCgojIyMgUTQuMSBTZXR1cCBUcmFpbmluZyAmIFRlc3RpbmcgRGF0YWZyYW1lcwoKUHJvY2Vzc2luZyB0aGUgZGF0YQoKICAtIGRpdmlkZSB0aGUgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0aW5nIGRhdGFzZXRzCiAgICAtIFVzaW5nIGBjYXJldDo6Y3JlYXRlRGF0YVBhcnRpdG9uKClgCiAgICAtIFdoaWNoIHdpbGwgZGl2aWRlIHRoZSBkYXRhIGludG8gZ3JvdXBzCiAgICAgIC0gU28gd2UgY2FuICJ0cmFpbiIgdGhlIG1vZGVsIG9uIG9uZSBzdWJzZXQKICAgICAgLSBBbmQgdXNlIHRoZSBvdGhlciBzdWJzZXQgdG8gInRlc3QiIHRoZSBtb2RlbHMgYWNjdXJhY3kKCmBgYHtyfQojIHBlcmZvcm0gYW5kIDgwLzIwIHNwbGl0IG9uIHBlbmd1aW5zIGRhdGEKIyBnb2FsOiBkZXRlcm1pbmUgc3BlY2llcyBvZiBwZW5ndWlucwpkZl9wZW5ndWlucyRzcGVjaWVzIDwtIGFzLmZhY3RvcihkZl9wZW5ndWlucyRzcGVjaWVzKQpwYXJ0aXRpb24gPC0gY3JlYXRlRGF0YVBhcnRpdGlvbihkZl9wZW5ndWlucyRzcGVjaWVzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcCA9IDAuOCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3QgPSBGQUxTRSkKCnBlbmd1aW5zX3RyYWluIDwtIGRmX3Blbmd1aW5zW3BhcnRpdGlvbiwgXQpwZW5ndWluc190ZXN0IDwtIGRmX3Blbmd1aW5zWy1wYXJ0aXRpb24sIF0KdGVzdF9zcGVjaWVzIDwtIHBlbmd1aW5zX3Rlc3Qkc3BlY2llcwpwZW5ndWluc190ZXN0IDwtIHBlbmd1aW5zX3Rlc3RbICwgLTFdCgpnbGltcHNlKHBlbmd1aW5zX3RyYWluKQpnbGltcHNlKHBlbmd1aW5zX3Rlc3QpCmBgYAoKCiMjIyBRNC4yIEJ1aWxkIGEgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbAoKQnVpbGQgYSBsb2dpc3RpYyBtb2RlbCAKCiAgLSBUbyBwcmVkaWN0IHNwZWNpZXMKICAtIEJhc2VkIG9uIGFsbCB0aGUgcHJlZGljdG9ycyBpbiB0aGUgZGF0YXNldAogICAgLSBUaGUgc2hvcnQgaGFuZCB3YXkgb2YgZG9pbmcgdGhpcyBpcyB0byB1c2UgCiAgICAtIGBtb2RlbF9mdW5jdGlvbl9jYWxsKFJlc3BvbnNlX1ZhcmlhYmxlIH4gLilgCiAgICAtIFdoZXJlIHRoZSBgLmAgd2lsbCBhdXRvbWF0aWNhbGx5IHVzZSB0aGUgcmVzdCBvZiB0aGUgY29sdW1ucwogICAgICAtIEFzIHByZWRpY3RvcnMKICAgICAgCk9uY2UgeW91IGhhdmUgb2J0YWluZWQgeW91ciBtb2RlbCBvYmplY3QKCiAgLSBJdCdzIHRpbWUgdG8gdXNlIGBzdGF0czo6cHJlZGljdCgpYAogICAgLSBXaGljaCB0YWtlcyBpbiBhIG1vZGVsIG9iamVjdAogICAgLSBUaGVuIGFwcGxpZXMgaXQgdG8gbmV3IGRhdGEgKHlvdXIgdGVzdGluZyBzdWJzZXQpCiAgLSBEaWZmZXJlbnQgbW9kZWwgdHlwZXMgY2FuIGhhdmUgZGlmZmVyZW50IHByZWRpY3Rpb24gY2xhc3NlcwogICAgLSBIZXJlIHdlIGFyZSB0cnlpbmcgdG8gcHJlZGljdCBjbGFzcwogICAgCgpgYGB7cn0Kc2V0LnNlZWQoNjkpCiMgQnVpbGQgeW91ciBtb2RlbApwZW5ndWluc19tb2RlbCA8LSBtdWx0aW5vbShzcGVjaWVzIH4gLiwgcGVuZ3VpbnNfdHJhaW4sIG1vZGVsID0gRkFMU0UpCmNhdCgiXG5tb2RlbCBzdW1tYXJ5OiBcbiIpCnN1bW1hcnkocGVuZ3VpbnNfbW9kZWwpCiMgUHJlZGljdCBjbGFzc2VzCnByZWRpY3Rpb25zIDwtIHByZWRpY3QocGVuZ3VpbnNfbW9kZWwsIHBlbmd1aW5zX3Rlc3QpCgojIHByZWRpY3Rpb25zIHZzIGFjdHVhbApjYXQoIlxucHJlZGljdGVkOiBcbiIpCnByZWRpY3Rpb25zCmNhdCgiXG5hY3R1YWw6IFxuIikKdGVzdF9zcGVjaWVzCmBgYAoKQU5TV0VSOiBMb29raW5nIGF0IHRoZSBvdXRwdXQgb2YgdGhlIG5ldXJhbCBuZXQgbW9kZWwsIHdlIGNhbiBzZWUgYWxsIHRoZSBtZXRhZGF0YSBmb3IgdHJhaW5pbmcsIHdoYXQgZ2VuZXJhbCBhY3RpdmF0aW9uIGZ1bmN0aW9uIChzb2Z0bWF4IGZ1bmN0aW9uKSBpcyB1c2VkLCBhbmQgb3RoZXIgaW5wdXRzIHRoYXQgY2FuIGJlIG1vZGlmaWVkICh3ZWlnaHRzIGFzIHdlbGwpLiBVc2luZyBhIHNlZWQgaGVyZSB0byBlbnN1cmUgc2FtZSByZXNwb25zZXMgYWZ0ZXIgbXVsdGlwbGUgcmVydW5zCgojIyMgUTQuMyBFdmFsdWF0ZSBhY2N1cmFjeSBvbiB5b3VyIHRlc3QgZGF0YQoKRXZhbHVhdGUgdGhlIGFjY3VyYWN5IG9mIHlvdXIgbW9kZWwgYWdhaW5zdCB0ZXN0IGRhdGEKCiAgLSBjcmVhdGUgYSBjb25mdXNpb24gbWF0cml4IHRvIGV2YWx1YXRlIHlvdXIgcmVzdWx0cwogIC0gdXNpbmcgYGNhcmV0Ojpjb25mdXNpb25NYXRyaXgoKWAKICAgIC0gVGhpcyBjb21wYXJlcyB0aGUgcHJlZGljdGVkIGNsYXNzIGFnYWluc3QgdGhlIGFjdHVhbCBjbGFzcwogICAgLSBXaGljaCBzaG93cyBob3cgdGhlIG1vZGVsIGNsYXNzaWZpZWQgdGhlIGRhdGEKCgpgYGB7cn0KIyBjb21wYXJpc29uIG9mIHByZWRpY3Rpb25zIHZzIGFjdHVhbApjb25mdXNpb25NYXRyaXgocHJlZGljdGlvbnMsIHJlZmVyZW5jZSA9IHRlc3Rfc3BlY2llcykKYGBgCgpBcmUgdGhlcmUgYW55IHRoaW5ncyB0aGF0IHdlcmUgY29tbW9ubHkgbWlzY2xhc3NpZmllZD8gCgogIC0gV2h5IGRvIHlvdSB0aGluayB0aGUgbW9kZWwgaGFkIHRyb3VibGUgd2l0aCB0aGVzZT8KICAtIFdoYXQgY2FuIGJlIGRvbmUgdG8gaW1wcm92ZSB0aGlzIG1vZGVsPyAKCkFOU1dFUjogCgpUaGUgbW9kZWwgd2FzIGFibGUgdG8gcHJlZGljdCB0aGUgc3BlY2llcyB3aXRoIDk2Ljk3JSAoY2xvc2UgdG8gMTAwJSkgYWNjdXJhY3kuIFRoZXJlIHdhc24ndCByZWFsbHkgYW55dGhpbmcgY29tbW9ubHkgbWlzY2xhc3NpZmllZCwgYW5kIGxvb2tpbmcgYXQgdGhlIHN0YXRpc3RpY3MgZnJvbSB0aGUgY29uZnVzaW9uIG1hdHJpeCwgb25seSAyIG9mIHRoZSAxMyB2YXJpYWJsZXMgd2VyZSBpbXByb3Blcmx5IGNsYXNzaWZpZWQgKDIgQ2hpbnN0cmFwcyB3ZXJlIGNsYXNzaWZpZWQgYXMgQWRlbGllKSwgbWFraW5nIGNoaW5zdHJhcCBoYXZlIGEgc2Vuc2l0aXZpdHkgYXQgODQlLiBQcm9iYWJseSBvbmUgb2YgdGhlIGJlc3Qgd2F5cyB0byBpbXByb3ZlIHRoZSBtb2RlbCBpcyBzZXR0aW5nIGN1c3RvbSBpbml0aWFsIHdlaWdodHMgd2hlbiB0cmFpbmluZyB0byBlbnN1cmUgcXVpY2sgY29udmVyZ2VuY2UsIHRodXMgZWxpbWluYXRpbmcgdGhlIHdlaWdodHMgdG8gYmUgc2V0IGF0IHJhbmRvbSwgd2hpY2ggbWF5IG9yIG1heSBub3QgYmUgZ29vZC4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBRNSBIb3VzdG9uIENyaW1lIFJlcG9ydHMKCldlIHdpbGwgYmUgd29ya2luZyB3aXRoIHRoZSBIdXN0b24gY3JpbWUgZGF0YSBmaWxlIHByb3ZpZGVkIGJ5IHRoZSBnZ21hcCBwYWNrYWdlLCBgZ2dtYXA6OmNyaW1lc2AuCgpUaGlzIENTViBmaWxlIGNvbnRhaW5zIHRoZSBsb2NhdGlvbiAobGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSkgZm9yIGNyaW1lcyByZXBvcnRlZCBmcm9tIEphbnVhcnkgMjAxMCAtIEF1Z3VzdCAyMDEwCgpgYGB7cn0KbGlicmFyeShnZ21hcCkKcmdkYWxfc2hvd19leHBvcnRUb1Byb2o0X3dhcm5pbmdzID0gIm5vbmUiCmxpYnJhcnkoc3ApCmxpYnJhcnkocmdkYWwpCmxpYnJhcnkobGVhZmxldCkKbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KGNsYXNzSW50KQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShSZmFzdCkKYGBgCgoKIyMjIFE1LjEgRURBIHRvIGlkZW50aWZ5IHRyZW5kcwoKRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcyAoRURBKQoKTGV0J3MgZG8gc29tZSBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzIG9uIHRoZSBkYXRhIHdlJ2xsIHN0YXJ0IHdpdGggdGhlIHRlbXBvcmFsIGFzcGVjdCBvZiB0aGUgY3JpbWUgZGF0YS4gV2hhdCBjYW4gd2Ugc2F5IGFib3V0ICoqd2hlbioqIHBlb3BsZSBjb21taXQgY3JpbWVzPwogIApXaGF0IHRyZW5kcyBkbyB5b3Ugc2VlIGxvb2tpbmcgYXQgZGlmZmVyZW50IHRpbWUgZnJhbWVzPwoKICAtIHdoYXQgbW9udGhzIGhhdmUgcGFydGljdWxhcmx5IGhpZ2ggY3JpbWUgcmF0ZXM/CiAgLSB3aGF0IHRpbWVzIG9mIGRheSBoYXZlIGluY3JlYXNlZCBjcmltZSByYXRlcz8KICAtIHdoYXQgZGF5cyBvZiB0aGUgd2VlayBoYXZlIGhpZ2hlciBjcmltZSByYXRlcz8KICAKICAtIHByb2R1Y2UgdGhyZWUgZGlmZmVyZW50IHZpc3VhbHMgdGhhdCByZXByZXNlbnQgZWFjaCBvZiB0aGVzZSB0cmVuZHMuCgooaGludDogaGlzdG9ncmFtcyBhcmUgaGVscGZ1bCBmb3Igc2hvd2luZyBkaXN0cmlidXRpb25zKQoKV2hpY2ggb2YgdGhlc2UgdHJlbmRzIGNvdWxkIHlvdSBoYXZlIHByZWRpY3RlZD8gRG9lcyBhbnl0aGluZyBzdXJwcmlzZSB5b3U/CgpBcmUgdGhlcmUgYW55IHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB0eXBlcyBvZiBjcmltZSBhbmQgdGltZSBvZiBkYXk/IFByb2R1Y2UgYSBzdGFja2VkIGhpc3RvZ3JhbSBhbmQgY29tbWVudCBvbiB0aGUgcmVzdWx0cwoKYGBge3J9CiMgTG9hZCBEYXRhCmhvdXN0b25fY3JpbWVfcmVwb3J0IDwtIGdnbWFwOjpjcmltZQojIGZpZWxkczoKIyB0aW1lLCBkYXRlLCBob3VyLCBtb250aCwgZGF5CiMgcHJlbWlzZSwgb2ZmZW5zZSwgYmVhdCwgbnVtYmVyIAojIGJsb2NrLCBzdHJlZXQsIHR5cGUsIHN1ZmZpeCwgbG9jYXRpb24sIGFkZHJlc3MKIyBsb24sIGxhdAojIGNvcHk6CmNyaW1lX3JlcG9ydCA8LSBob3VzdG9uX2NyaW1lX3JlcG9ydAoKIyBDcmltZSBwZXIgRGF5CmdncGxvdChjcmltZV9yZXBvcnQsIGFlcyh4ID0gZGF5LCBmaWxsID0gYXMuZmFjdG9yKG9mZmVuc2UpKQogICAgICAgKSArIGdlb21faGlzdG9ncmFtKHN0YXQgPSAiY291bnQiCiAgICAgICApICsgZ2d0aXRsZSgiY3JpbWVzIHBlciBkYXkiCiAgICAgICApICsgeGxhYigiZGF5IgogICAgICAgKQoKIyBMZXRzIERvIHNvbWUgRURBCiMgTG9va2luZyBhdCB0aGUgdHlwZXMgb2YgQ3JpbWUgb24gYSBnaXZlbiBkYXkKcGxvdF9kZXNpcmVkX2NyaW1lc19mb3JfZGF5IDwtIGZ1bmN0aW9uKGRlc2lyZWRfZGF5KSB7CiAgY3JpbWVzX2Zvcl9kYXkgPC0gc3Vic2V0KGNyaW1lX3JlcG9ydCwgZGF5ID09IGRlc2lyZWRfZGF5KQogIGNyaW1lc190aXQgPC0gcGFzdGUoInR5cGVzIG9mIGNyaW1lcyBmb3IiLCBkZXNpcmVkX2RheSwgc2VwID0gIiAiKQogIGdncGxvdChjcmltZXNfZm9yX2RheSwgYWVzKHggPSBvZmZlbnNlKQogICAgICAgICApICsgZ2VvbV9oaXN0b2dyYW0oc3RhdCA9ICJjb3VudCIKICAgICAgICAgKSArIGdndGl0bGUoY3JpbWVzX3RpdAogICAgICAgICApICsgeGxhYigib2ZmZW5zZSIpCn0KCnBsb3RfZGVzaXJlZF9jcmltZXNfZm9yX2RheSgibW9uZGF5IikKcGxvdF9kZXNpcmVkX2NyaW1lc19mb3JfZGF5KCJ0dWVzZGF5IikKCnBsb3RfY29ycmVsYXRpb25fZm9yX2RheSA8LSBmdW5jdGlvbihkZXNpcmVkX2RheSkgewogIGNyaW1lc19mb3JfZGF5IDwtIHN1YnNldChjcmltZV9yZXBvcnQsIGRheSA9PSBkZXNpcmVkX2RheSkKICBjcmltZXNfdGl0IDwtIHBhc3RlKCJ0eXBlcyBvZiBjcmltZXMgZm9yIiwgCiAgICAgICAgICAgICAgICAgICAgICBkZXNpcmVkX2RheSwgCiAgICAgICAgICAgICAgICAgICAgICAiYnkgaG91ciIsIAogICAgICAgICAgICAgICAgICAgICAgc2VwID0gIiAiKQogIGdncGxvdChjcmltZXNfZm9yX2RheSwgYWVzKHggPSBob3VyLCBmaWxsID0gYXMuZmFjdG9yKG9mZmVuc2UpKQogICAgICAgICApICsgZ2VvbV9oaXN0b2dyYW0oc3RhdCA9ICJjb3VudCIsIHBvc2l0aW9uID0gInN0YWNrIgogICAgICAgICApICsgZ2d0aXRsZShjcmltZXNfdGl0CiAgICAgICAgICkgKyB4bGFiKCJvZmZlbnNlIikKfQoKcGxvdF9jb3JyZWxhdGlvbl9mb3JfZGF5KCJtb25kYXkiKQpwbG90X2NvcnJlbGF0aW9uX2Zvcl9kYXkoInR1ZXNkYXkiKQoKIyBOb3cgbGV0cyBsb29rIGF0IHRoZSBNb250aApnZ3Bsb3QoY3JpbWVfcmVwb3J0LCBhZXMoeCA9IG1vbnRoLCBmaWxsID0gYXMuZmFjdG9yKG9mZmVuc2UpKQogICAgICAgKSArIGdlb21faGlzdG9ncmFtKHN0YXQgPSAiY291bnQiCiAgICAgICApICsgZ2d0aXRsZSgiY3JpbWVzIHBlciBtb250aCIKICAgICAgICkgKyB4bGFiKCJtb250aCIpCgpwbG90X2Rlc2lyZWRfY3JpbWVzX2Zvcl9tb250aCA8LSBmdW5jdGlvbihkZXNpcmVkX21vbnRoKSB7CiAgY3JpbWVzX2Zvcl9tb250aCA8LSBzdWJzZXQoY3JpbWVfcmVwb3J0LCBtb250aCA9PSBkZXNpcmVkX21vbnRoKQogIGNyaW1lc190aXQgPC0gcGFzdGUoInR5cGVzIG9mIGNyaW1lcyBmb3IiLCBkZXNpcmVkX21vbnRoLCBzZXAgPSAiICIpCiAgZ2dwbG90KGNyaW1lc19mb3JfbW9udGgsIGFlcyh4ID0gb2ZmZW5zZSkKICAgICAgICAgKSArIGdlb21faGlzdG9ncmFtKHN0YXQgPSAiY291bnQiCiAgICAgICAgICkgKyBnZ3RpdGxlKGNyaW1lc190aXQKICAgICAgICAgKSArIHhsYWIoIm9mZmVuc2UiKQp9CgpwbG90X2Rlc2lyZWRfY3JpbWVzX2Zvcl9tb250aCgiamFudWFyeSIpCnBsb3RfZGVzaXJlZF9jcmltZXNfZm9yX21vbnRoKCJmZWJydWFyeSIpCnBsb3RfZGVzaXJlZF9jcmltZXNfZm9yX21vbnRoKCJtYXJjaCIpCmBgYAoKQU5TV0VSOiAKCldoYXQgd2UgY2FuIHNheSBhYm91dCAqKndoZW4qKiBwZW9wbGUgY29tbWl0IGNyaW1lczoKCk1vc3QgbW9udGhzIGFuZCBkYXlzIGZvbGxvdyBhIHNpbWlsYXIgcGF0dGVybi4KClRoZWZ0IHNlZW1zIHRvIGJlIHRoZSBtb3N0IGNvbW1vbiBvZmZlbnNlLCB3aXRoIGJ1cmdsYXJ5IGNvbWluZyBpbiBhZnRlcgoKVGhlIG1vbnRoIE1heSBzZWVtcyB0byBoYXZlIHRoZSBoaWdoZXN0IGNyaW1lIHJhdGVzCgpUb3dhcmRzIHRoZSBhZnRlcm5vb24gaW50byB0aGUgZXZlbmluZywgdGhlcmUgYXJlIGluY3JlYXNlZCBjcmltZSByYXRlcwoKVGh1cnNkYXksIEZyaWRheSwgU2F0dXJkYXkgaGF2ZSBnZW5lcmFsbHkgaGlnaGVyIGNyaW1lIHJhdGVzIHRoYW4gb3RoZXIgZGF5cyAKCkkgaG9uZXN0bHkgY291bGRuJ3QgaGF2ZSBwcmVkaWN0ZWQgYW55IG9mIHRoaXMsIGFuZCBub25lIHN1cnByaXNlIG1lCgpUaGVyZSBpcyBhIG5vcm1hbCByZWxhdGlvbnNoaXAgYmV0d2VlbiB0eXBlcyBvZiBjcmltZSBhbmQgdGltZSBvZiBkYXkKCgojIyMgUTUuMiBHZW9zcGF0aWFsIEFuYWx5c2lzCgpgYGB7cn0KbGlicmFyeShzcCkKbGlicmFyeShyZ2RhbCkKbGlicmFyeShtYXB0b29scykKYGBgCgpHZW9zcGF0aWFsIEFuYWx5c2lzCgpOZXh0IHdlJ2xsIGxvb2sgYXQgdGhlIHNwYXRpYWwgZGlzdHJpYnV0aW9uIG9mIHRoaXMgZGF0YS4KCiAgLSBQbG90IHRoZSBkYXRhIG9uIGFuIE9wZW5TdHJlZXRNYXAgKCoqRXJyb3I6ICdnZXRfb3BlbnN0cmVldG1hcCcgaXMgZGVmdW5jdC4KVXNlICdPU00gaXMgYXQgbGVhc3QgdGVtcG9yYXJpbHkgbm90IHN1cHBvcnRlZCwgc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9ka2FobGUvZ2dtYXAvaXNzdWVzLzExNy4nIGluc3RlYWQuClNlZSBoZWxwKCJEZWZ1bmN0IikqKikKICAgIC0gVXNpbmcgYHNvdXJjZSA9ICJzdGFtZW4iYAogIC0gWW91IHdpbGwgaGF2ZSB0byBzcGVjaWZ5IHRoZSBsb2NhdGlvbiBpbiB0aGUgZnVuY3Rpb24gY2FsbAogICAgICAtIFRoaXMgaXMgYmVjYXVzZSBgZ2dtYXA6OmdldF9tYXAoKWAKICAgICAgLSBEZWZhdWx0cyB0byBHb29nbGUgTWFwcyAKICAgICAgICAtIHdoZW4gYSBib3VuZGluZyBib3ggaXMgbm90IHNwZWNpZmllZAogICAgICAtIEEgYm91bmRpbmcgYm94IAogICAgICAgIC0gR2l2ZXMgdGhlIGJvdW5kYXJpZXMgb2YgdGhlIG1hcCB0aGF0IGlzIGRvd25sb2FkZWQKICAgICAgICAtIFNwZWNpZmllZCB3aXRoIGEgbGlzdAogICAgICAgIC0gYGMobGVmdCA9ICcnICwgcmlnaHQgPSAnJyAsIHRvcCA9ICcnLCBib3R0b20gPSAnJylgCiAgLSBDb2xvciB0aGUgbWFwIGJhc2VkIG9uIAogICAgLSBUeXBlIG9mIENyaW1lIFJlcG9ydGVkCiAgCgpgYGB7cn0KI0dldCB0aGUgYmJveAoKYmJveCA8LSBjKGxlZnQgPSAtOTYsIAogICAgICAgICAgcmlnaHQgPSAtOTUsCiAgICAgICAgICB0b3AgPSAzMC4yNSwKICAgICAgICAgIGJvdHRvbSA9IDI5LjI1KQoKI1JldHJpZXZlIHRoZSBtYXAKCmhvdXN0b25tYXAgPC0gZ2V0X21hcChsb2NhdGlvbiA9IGJib3gsCiAgICAgICAgICAgICAgICAgICAgICBzb3VyY2UgPSAnc3RhbWVuJykKCiNQbG90CgpjcmltZV9yZXBvcnQkb2ZmZW5zZSA8LSBhcy5mYWN0b3IoY3JpbWVfcmVwb3J0JG9mZmVuc2UpCgpnZ21hcChob3VzdG9ubWFwKSArIGdlb21fcG9pbnQoYWVzKHggPSBsb24sIHkgPSBsYXQsIGNvbG9yID0gb2ZmZW5zZSksIGRhdGEgPSBjcmltZV9yZXBvcnQpCmBgYAoKQU5TV0VSOiBwbG90dGVkCgojIyMgUTUuM2EgTW9kaWZ5IHRoZSBpbmNpZGVudCBvY2N1cnJlbmNlIGxheWVyLCB0byBiZXR0ZXIgc2VlIHdoYXRzIGhhcHBlbmluZwoKSW4gdGhlIGxhc3QgbWFwLCBpdCB3YXMgYSBiaXQgdHJpY2t5IAoKICAtIHRvIHNlZSB0aGUgZGVuc2l0eSBvZiB0aGUgaW5jaWRlbnRzIAogICAgLSBiZWNhdXNlIGFsbCB0aGUgZ3JhcGhlZCBwb2ludHMgCiAgICAtIHdlcmUgc2l0dGluZyBvbiB0b3Agb2YgZWFjaCBvdGhlci4gIAoKV2UncmUgZ29pbmcgdG8gbm93IG1vZGlmeSB0aGUgaW5jaWRlbnQgb2NjdXJyZW5jZSBsYXllciAKCiAgLSB0byBwbG90IHRoZSBkZW5zaXR5IG9mIHBvaW50cyAKICAgIC0gdnMgcGxvdHRpbmcgZWFjaCBpbmNpZGVudCBpbmRpdmlkdWFsbHkuICAKICAtIFdlIGFjY29tcGxpc2ggdGhpcyB3aXRoIAogICAgLSB0aGUgYGdncGxvdDI6OnN0YXRfZGVuc2l0eTJkKClgIGZ1bmN0aW9uIAogICAgLSB2cyB1c2luZyBgZ2dwbG90Mjo6Z2VvbV9wb2ludCgpYC4KICAKCmBgYHtyfQojUGxvdCB1c2luZyBzdGF0X2RlbnNpdHkyZCgpCmdnbWFwKGhvdXN0b25tYXApICsgc3RhdF9kZW5zaXR5MmQoYWVzKHggPSBsb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBsYXQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IG9mZmVuc2UpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gY3JpbWVfcmVwb3J0KQpgYGAKCkFOU1dFUjoKCiMjIyBRNS4zYiAgV2hhdCBkb2VzIGAuLmxldmVsLi5gIGRvCgogIC0gV2hhdCBkb2VzIGAuLmxldmVsLi5gIGRvCiAgICAtIGluIHRoZSBgZ2dwbG90Mjo6c3RhdF9kZW5zaXR5MmQoKWAgZnVuY3Rpb24gY2FsbD8KICAtIEhpbnQ6IExvb2sgYXQgdGhlIGhlbHAgdG9waWNzIGZvciB0aGlzIGZ1bmN0aW9uCgpgYGB7cn0KZ2dtYXAoaG91c3Rvbm1hcCkgKyBzdGF0X2RlbnNpdHkyZChhZXMoeCA9IGxvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBsYXQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IG9mZmVuc2UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gLi5sZXZlbC4uKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGNyaW1lX3JlcG9ydAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgKyBzdGF0X2RlbnNpdHkyZChhZXMoIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gLi5sZXZlbC4uKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCmBgYAoKQU5TV0VSOiAuLmxldmVsLi4gdGVsbHMgZ2dtYXAgdG8gcmVmZXJlbmNlIHRoZSBjb2x1bW4gaW4gYSBuZXdseSBidWlsdCBkYXRhIGZyYW1lLCBhbmQgc2tldGNoZXMgY29udG91ciBsaW5lcwoKIyMjIFE1LjQgV2hhdCBpcyB0aGUgc2FmZXN0IGFuZCBtb3N0IGRhbmdlcm91cyBuZWlnaGJvcmhvb2RzCgpgYGB7cn0KI0ZpbHRlcgpsaWJyYXJ5KGJyb29tKQpsaWJyYXJ5KGdncGxvdDIpCmBgYAoKRmluYWxseSAKCiAgLSBGaWx0ZXIgb3V0IGEgc3BlY2lmaWMgY3JpbWUgb2YgeW91ciBjaG9vc2luZyAoYXV0by10aGVmdCkKICAtIFBsb3QgdGhlIGNyaW1lIGRlbnNpdHkKICAKV2Ugd2lsbCB1c2UgYSBuZXcgcGFja2FnZSB0aGF0IGFzc2lzdHMgaW4gZ2Vvc3BhdGlhbCBhbmFseXNpcwoKICAtIGByZGdhbGAKICAtIFRoaXMgcGFja2FnZSBpcyB1c2VkIHRvIHRyYW5zZm9ybSBhbmQgcHJvamVjdCBnZW9zcGF0aWFsIG9iamVjdHMKICAtIEl0IGFsc28gaGFzIHNvbWUgbmljZSBmdW5jdGlvbnMgZm9yIHdvcmtpbmcgd2l0aCBgLnNocGAgZmlsZXMKICAgIC0gYC5zaHBgIGZpbGVzIGNvbnRhaW4gaW5mb3JtYXRpb24gYWJvdXQgcmVnaW9ucyBvbiBhIG1hcAogICAgLSBpLmUgYC5zaHBgIGZpbGVzIGNhbiBjb250YWluIHRoZSBpbmZvcm1hdGlvbgogICAgICAtIHRoZSBzaXplLCBzaGFwZSwgYW5kIGxvY2F0aW9uIG9mIGNvdW50cmllcyBvciBzdGF0ZXMKCkFkZCBQb2x5Z29ucyBmb3IgdGhlIHNwZWNpZmljIE5laWdoYm9yaG9vZHMKCiAgLSBVc2luZyBOZWlnaGJvclNoYXBlZmlsZQogICAgLSBhbmQgYHJkZ2FsOjpyZWFkT0dSKClgCiAgICAtIG9yIGBtYXBkYXRhOjpyZWFkU2hhcGVTcGF0aWFsKClgCiAgCmBgYHtyfQpuYmhkX3BhdGggPC0gJ2RhdGEvc2hwL0NPSF9TVVBFUl9ORUlHSEJPUkhPT0RTLnNocCcKbmJoZF9maWxlIDwtIHJlYWRPR1IobmJoZF9wYXRoKQpuYmhkcyA8LSB0aWR5KG5iaGRfZmlsZSkKbmFtZXMobmJoZF9maWxlKQphdXRvX3RoZWZ0cyA8LSBzdWJzZXQoY3JpbWVfcmVwb3J0LCBvZmZlbnNlID09ICdhdXRvIHRoZWZ0JykKZmlsdGVyZWRfYXV0b190aGVmdHMgPC0gYXMuZGF0YS5mcmFtZShhcy50aWJibGUoYXV0b190aGVmdHMpICU+JSBzZWxlY3QobnVtYmVyLCBsb24sIGxhdCwgYmxvY2ssIHByZW1pc2UpKQoKCmlkX25hbWVfbWFwIDwtIGRhdGEuZnJhbWUoCiAgaWQgPSBuYmhkX2ZpbGUkUE9MWUlELAogIG5hbWUgPSBuYmhkX2ZpbGUkU05CTkFNRQopCgpuYmhkcyA8LSBtZXJnZShuYmhkcywgaWRfbmFtZV9tYXAsIGJ5LnggPSAnaWQnKQpgYGAKICAgICAgCldoYXQgaXMgdGhlIG1vc3QgZGFuZ2Vyb3VzIE5laWdoYm9yaG9vZCBmb3IgeW91ciBjcmltZT8gV2hlcmUgaXMgdGhlIHNhZmVzdCBuZWlnaGJvcmhvb2Q/CgpMYWJlbCB0aGUgbWFwIHdpdGggdGhlIG5laWdoYm9yaG9vZHMKCiAgLSBIaW50OiBJdCdzIE9LIHRvIHJlbW92ZSBzb21lIG9mIHRoZSBsYWJlbHMsIHRoZXJlJ3MgYSBsb3QKICAtIGBnZ3Bsb3QyOjpnZW9tX3RleHQoKWAgaGFzIGEgYnVpbHQgaW4gZnVuY3Rpb24gZm9yIHRoaXMKICAgICAgLSBjaGVjayBgb3ZlcmxhcCA9IFRSVUVgCiAgICAgIAoKYGBge3J9CmdnbWFwKGhvdXN0b25tYXAKKSArIGdlb21fcG9seWdvbihkYXRhID0gbmJoZHMsIAogICAgICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGxvbmcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGxhdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cCA9IGdyb3VwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGlkKSwgCiAgICAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGQUxTRQopICArIGdlb21fcG9pbnQoZGF0YSA9IGF1dG9fdGhlZnRzLCAKICAgICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGxvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBsYXQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLCAKICAgICAgICAgICAgICAgIGNvbG9yID0gImxpZ2h0Z3JleSIsCiAgICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEZBTFNFCikgKyBnZW9tX3RleHQoZGF0YSA9IG5iaGRzLCAKICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBsb25nLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBsYXQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBuYW1lKSwKICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEZBTFNFLAogICAgICAgICAgICAgIGNoZWNrX292ZXJsYXAgPSBUUlVFLAogICAgICAgICAgICAgICMgdmp1c3QgPSAiaW53YXJkIiwgCiAgICAgICAgICAgICAgIyBoanVzdCA9ICJpbndhcmQiCikKCmBgYAogICAgCkFOU1dFUjogCgpMb29rcyBsaWtlIG1pZHRvd24td2lsbG93YnJvb2ssIGFuZCBraW5nd29vZCBhcmVhIGFyZSBzb21lIG9mIHRoZSBtb3N0IGRhbmdlcm91cyBuZWlnaGJvcmhvb2RzIGZvciBhdXRvLXRoZWZ0LCB3aGVyZWFzIGZvbmRyZW4gZ2FyZGVucywgd2lsbG93YmVuZCBhcmVhLCB3ZXN0YnJhbmNoLCBhbmQgY2VudHJhbCBzb3V0aHdlc3QgYXJlIHNvbWUgb2YgdGhlIHNhZmVzdC4KCgojIyMgTGlua3MKCltodHRwczovL2Jsb2cuZG9taW5vZGF0YWxhYi5jb20vYXBwbGllZC1zcGF0aWFsLWRhdGEtc2NpZW5jZS13aXRoLXIvXShodHRwczovL2Jsb2cuZG9taW5vZGF0YWxhYi5jb20vYXBwbGllZC1zcGF0aWFsLWRhdGEtc2NpZW5jZS13aXRoLXIvKQoKW2h0dHA6Ly93d3cuci1wcm9qZWN0Lm9yZ10oaHR0cDovL3d3dy5yLXByb2plY3Qub3JnKSAKCltodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tL10oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS8pICAKCltodHRwczovL3d3dy5vcGVuaW50cm8ub3JnL3N0YXQvdGV4dGJvb2sucGhwP3N0YXRfYm9vaz1vc10oaHR0cHM6Ly93d3cub3BlbmludHJvLm9yZy9zdGF0L3RleHRib29rLnBocD9zdGF0X2Jvb2s9b3MpCgpbaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTXVsdGl2YXJpYXRlX2tlcm5lbF9kZW5zaXR5X2VzdGltYXRpb25dKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL011bHRpdmFyaWF0ZV9rZXJuZWxfZGVuc2l0eV9lc3RpbWF0aW9uKQ==